const CURRENT_USER = '当前用户姓名'
const getEnvInfoFn = function(models) {
    return function() {
        return {
            [CURRENT_USER]: ((models || {}).env || {}).nowOpt || ''
        }
    }
}
let getEnvInfo = null

/**
 * 合并对象属性返回新对象
 * @param { Object } n个参数且是对象
 * @return { Object } 返回多个对象的集合
 */
const mergeObject = function() {
    return [...arguments].reduce((ret, obj) => {
        return { ...ret, ...obj }
    }, {})
}

/**
 * 运行函数
 * @param { Function } anonymousFn 函数
 * @param { Object } context 上下文环境
 * @return { Boolean }
 */
const runAnonymousFn = function(anonymousFn, context) {
    context = context || window
    try {
        return anonymousFn.call(context) || false
    } catch(e) {
        return false
    }
}

// 以下对表达式进行替换
function handlerCurlyBraces(expression) {
    return expression.replace(/\{([\w\W]*)\}/, (a, b) => b)
}
function handlerExpressionContain(expression, key = 'contain') {  // /\.contain\([^)]*\)/g
    return expression.replace(/\.contain\([^)]*\)/g, (a) => handlerReplace(a, key))
}
function handlerExpressionNotContain(expression, key = 'notContain') { // /\.contain\([^)]*\)|\.notContain\([^)]*\)/g
    return expression.replace(/\.notContain\([^)]*\)/g, (a) => handlerReplace(a, key))
}
function handlerReplace(str, key) {
    let arr = str.match(/\([^)]*\)/g) || []
    str = arr[0]
    if (str) {
        str = str.slice(1, str.length - 1).split(',').map((item) => {
            return `'${item.replace(/['"‘’“” ]/g, '')}'`
        }).join(',')
    }
    return `.${key}(${str})`
}
function twoDigit(num) {
    return String.length < 2 ? '0' + num : num
}
const DATE_TEXT1 = '当前日期', DATE_TEXT2 = '当前日期时间'
function handlerExpressionDate(expression, key = DATE_TEXT1) {
    if (key === DATE_TEXT2) {
        return expression.replace(key, +new Date())
    } else if (key === DATE_TEXT1) {
        let now = new Date(), year = now.getFullYear(),  month = now.getMonth() + 1, date = now.getDate()
        return expression.replace(key, +new Date(`${year}-${twoDigit(month)}-${twoDigit(date)}`))
    } else {
        return expression
    }
}
function handlerExpressionCurrentUser(expression) {
    return expression.replace(CURRENT_USER, `'${getEnvInfo()[CURRENT_USER].replace(/['"‘’“” ]/g, '')}'`)
}
function handlerExpression(expression) {
    expression = handlerCurlyBraces(expression) // 把 {  } 去掉 
    expression = handlerExpressionContain(expression) // 处理 contain 表达式
    expression = handlerExpressionNotContain(expression) // 处理 notContain 表达式
    expression = handlerExpressionDate(handlerExpressionDate(expression, DATE_TEXT1), DATE_TEXT2) // 处理 系统参数 当前日期 当前日期时间
    expression = handlerExpressionCurrentUser(expression) // // 处理 当前用户姓名
    return expression
}

/**
 * 获取表达式相关信息
 * @param { String } expression 用户填写的表达式规则
 * @param { Object } infos { name: value } 把表达式expression中name替换相应的value
 * @return { Object }
 */
const getCalculateInfo = function(expression, infos, metas) {
    expression = handlerExpression(expression)
    const sorted = (metas || []).map(item => item.name).sort((a, b) => b.length - a.length)
    sorted.forEach(key => {
        let value = infos[key]
        expression = expression.replace(new RegExp(key, 'g'), `getValue('${value}')`)
    })

    let code = `
        Number.prototype.contain = 
        String.prototype.contain = function() {
            let arr = String(this).split(','), args = [...arguments], result = false
            // if (arr.filter(m => m !== '').length == 0) return false
            ;for (let i = 0; i < args.length; i++) {
                let bool = arr.some((item) => { return item == args[i] })
                if (bool) {
                    result = true
                    break
                }
            }
            return result
        }

        Number.prototype.notContain = 
        String.prototype.notContain = function() {
            return !String.prototype.contain.apply(this, arguments)
        }
    
        function isNumber(val) {
            return !isNaN(Number(val))
        }
  
        function getValue(val) {
            if (val === '') {
                return val
            }
            if (val === undefined) {
                return 0
            }
            // 兼容如： "1,000.00"类数据
            if (val.indexOf('.0') !== -1 || val.indexOf('.00') !== -1) {
                val = val.replace(',', '')
            }
    
            if (isNumber(val)) {
                return isNaN(val) ? val : (+val)
            }
            return val
        }
        return ${expression}
    `

    return {
        calcRet: runAnonymousFn(new Function(code)),
        calcExpression: expression
    }
}

/**
 * 获取校验结果信息
 * @param { Object } validateInfos { expression, tip }
 * @param { Object } infos { name: value } 把表达式expression中name替换相应的value
 * @return { Object }
 */
const getResult = function(validateInfos, infos, metas) {
    let { expression, tip } = validateInfos
    let calculateInfo = getCalculateInfo(expression, infos, metas)

    return {
        expression,
        tip,
        ...calculateInfo
    }
}

// 表单中的日期值转换成时间戳
function handlerFormData(formData) {
    Object.keys(formData).forEach((key) => {
        let value = formData[key]
        if (/\d{4}-/g.test(value)) {
            formData[key] = +new Date(value)
        }
    })
}

/**
 * 校验业务字段是否通过
 * @param { Object } formData { name: value } sub-form各个控件名称对应的值
 * @param { Object } sameFormExt { datasource: 'JSON字符串', businessValidate: [{ expression: '用户输入的表达式', name: '校验名', tip: '当expression成立时需要提示的信息', triggerType: '触发类型' }] }
 * @param { Object } models { meta: [{ name }] }
 * @return { Object } // { ret, tip } ret： true: 通过 false: 不通过  tip: 提示
 */
const TIP_TEXT = '参数有误!'
export function validator(formData, sameFormExt, models) {
    // 简单的参数校验
    const args = Array.from(arguments)
    if (!args.slice(0, 3).every(
        obj => Object.prototype.toString.call(obj) === '[object Object]' && Object.keys(obj).length > 0
    )) {
        console.warn(TIP_TEXT)
        return { ret: false, tip: '' }
    }

    let { businessValidate } = sameFormExt
    handlerFormData(formData)
    getEnvInfo = getEnvInfoFn(models)
    let dataInfo = mergeObject(formData) // 把表单数据和系统数据合并
    // console.log('formData: ', dataInfo)
    let tip = ''
    let result = businessValidate.some((item) => {
        let ret = getResult(item, dataInfo, models.meta)
        tip = ret.tip
        // console.log('ret: ', ret)
        return ret.calcRet
    })
    return { ret: !result, tip }
}